package shutdown

import (
	"context"
	"time"

	"github.com/mars/pkg/loggers"
)

// GSInterface defines the action for a shutdown request
type GSInterface interface {
	// DoShutdown is executed when shutdown request occur. DoShutdown calls all ShutdownCallback.OnShutdown
	DoShutdown(sm ShutdownManager)

	// ReportError handle the error when shutdown action occur error
	ReportError(err error)

	// AddShutdownCallback add a ShutdownCallback, the OnShutdown method will be called in StartShutdown
	AddShutdownCallback(shutdownCallback ShutdownCallback)
}

type ShutdownManager interface {
	// GetName returns the name of ShutdownManager
	GetName() string

	// WaitShutdownRequest register GSInterface and listen to the shutdown signal
	ListenShutdown(gs GSInterface) error

	// ShutdownStart begins to shutdown.
	ShutdownStart() error

	// ShutdownFinish finished the shutdown
	ShutdownFinish() error
}

type ShutdownCallback interface {
	OnShutdown(string) (string, error)
}

// ShutdownFunc is a helper type, so you can easily provide anonymous functions
// as ShutdownCallbacks.
type ShutdownFunc func(string) (string, error)

// OnShutdown defines the action needed to run when shutdown triggered.
func (f ShutdownFunc) OnShutdown(shutdownManager string) (string, error) {
	return f(shutdownManager)
}

type ErrorHandler interface {
	OnError(err error)
}

// ErrorFunc is a helper type, so you can easily provide anonymous functions
// as ErrorHandlers.
type ErrorFunc func(err error)

// OnError defines the action needed to run when error occurred.
func (f ErrorFunc) OnError(err error) {
	f(err)
}

// GracefulShutdown
type GracefulShutdown struct {
	callbacks    []ShutdownCallback
	managers     []ShutdownManager
	errorHandler ErrorHandler
}

var _ GSInterface = (*GracefulShutdown)(nil)

func New() *GracefulShutdown {
	return &GracefulShutdown{
		callbacks: make([]ShutdownCallback, 0, 10),
		managers:  make([]ShutdownManager, 0, 1),
		errorHandler: ErrorFunc(func(err error) {
			loggers.Errorf("shutdown error hanlder: %v", err)
		}),
	}
}

// Start calls all ShutdownManager.start method. ShutdownManager start to listen the shutdown signal.
func (gs *GracefulShutdown) Start() error {
	for _, manager := range gs.managers {
		if err := manager.ListenShutdown(gs); err != nil {
			return err
		}
	}
	return nil
}

func (gs *GracefulShutdown) AddShutdownManager(manager ShutdownManager) {
	gs.managers = append(gs.managers, manager)
}

func (gs *GracefulShutdown) AddShutdownCallback(callback ShutdownCallback) {
	gs.callbacks = append(gs.callbacks, callback)
}

func (gs *GracefulShutdown) SetErrorHandler(errHandler ErrorHandler) {
	gs.errorHandler = errHandler
}

func (gs *GracefulShutdown) DoShutdown(sm ShutdownManager) {
	gs.ReportError(sm.ShutdownStart())

	for _, callback := range gs.callbacks {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
		go func() {
			defer cancel()
			gs.ReportNameAndError(callback.OnShutdown(sm.GetName()))
		}()
		<-ctx.Done()
	}
	gs.ReportError(sm.ShutdownFinish())
}

func (gs *GracefulShutdown) ReportError(err error) {
	if err != nil && gs.errorHandler != nil {
		gs.errorHandler.OnError(err)
	}
}

func (gs *GracefulShutdown) ReportNameAndError(name string, err error) {
	loggers.Infof("shutdown component: `%s` finished...", name)
	if err != nil && gs.errorHandler != nil {
		gs.errorHandler.OnError(err)
	}
}
